04.自定义属性

自定义属性

自定义属性步骤

系统自带的 View 可以在 xml 中配置属性,对于写的好的自定义 View 同样可以在 xml 中配置属性,为了使自定义的
View 的属性可以在 xml 中配置,需要以下 4 个步骤:

  1. attrs.xml 通过 <declare-styleable> 为自定义 View 添加属性
  2. 在 xml 中为相应的属性声明属性值(注意 namespace)
  3. 在运行时(一般为构造函数)通过 TypedArray 获取获取属性值
  4. 将获取到的属性值应用到 View

Android 自定义控件之自定义属性及自定义属性定义 style

系统 attrs.xml 路径

D:\Android_sdk\sdk\platforms\android-27\data\res\values\attrs.xml

attrs.xml 定义自定义属性值

attrs.xml 文件定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RatioImageView">
        <attr name="ratio" format="float"/>
    </declare-styleable>
    <declare-styleable name="TaskItemView">
        <attr name="task_item_title" format="string"/>
        <attr name="task_item_titleColor" format="color"/>
        <attr name="task_item_titleTextSize" format="dimension"/>
        <attr name="task_item_desc" format="string"/>
        <attr name="task_item_descColor" format="color"/>
        <attr name="task_item_descTextSize" format="dimension"/>
        <attr name="task_item_descIcon" format="reference"/>
        <attr name="task_item_isDescIcon" format="boolean"/>
        <attr name="task_item_isDesc" format="boolean"/>
        <attr name="task_item_isArrow" format="boolean"/>
        <attr name="task_item_isDivider" format="boolean"/>
    </declare-styleable>
</resources>

获取自定义属性值

Java 代码获取:

private String mTitle;
private String mDesc;
private int mTitleColor;
private int mDescColor;
private boolean isDivider;
private boolean isDesc;
private boolean isArrow;
private boolean isDescIcon;
private float mTextSize;
private float mDescSize;
private int mDescIconResId;
private void init(Context context, AttributeSet attrs) {
    mInflater = LayoutInflater.from(getContext());

    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TaskItemView);
    try {
        mTitle = ta.getString(R.styleable.TaskItemView_task_item_title);
        mDesc = ta.getString(R.styleable.TaskItemView_task_item_desc);
        mTitleColor = ta.getColor(R.styleable.TaskItemView_task_item_titleColor, Color.BLACK);
        mDescColor = ta.getColor(R.styleable.TaskItemView_task_item_descColor, Color.BLACK);
        isDivider = ta.getBoolean(R.styleable.TaskItemView_task_item_isDivider, DEFAULT_DIVIDER_SHOW);
        isDesc = ta.getBoolean(R.styleable.TaskItemView_task_item_isDesc, DEFAULT_DESC_SHOW);
        isArrow = ta.getBoolean(R.styleable.TaskItemView_task_item_isArrow, DEFAULT_ARROW_SHOW);
        isDescIcon = ta.getBoolean(R.styleable.TaskItemView_task_item_isDescIcon, DEFAULT_ARROW_SHOW);
        mTextSize = ta.getDimensionPixelSize(R.styleable.TaskItemView_task_item_titleTextSize, TITLE_TEXT_SIZE_DEFAULT_SP);
        mDescSize = ta.getDimensionPixelSize(R.styleable.TaskItemView_task_item_descTextSize, DESC_TEXT_SIZE_DEFAULT_SP);
        mDescIconResId = ta.getResourceId(R.styleable.TaskItemView_task_item_descIcon, R.drawable.ic_task_item_red_packet);
    } finally {
        ta.recycle();
    }
}

format 可选值

string:字符串
reference:引用资源
color:颜色
boolean:布尔值
dimension:尺寸值
float:浮点型
integer:整型
fraction:百分数
enum:枚举类型
flag:位或运算

string 字符串

代码获取 getString()

reference 引用

代码获取 Drawable getDrawable(@StyleableRes int index)
或者 int getResourceId(@StyleableRes int index, int defValue)

color 颜色值

getColor()

boolean 布尔值

getBoolean()

dimension 尺寸值

getDimension()

float 浮点型

getFloat()

integer 整数值

getInt() 或者 getInteger()

fraction 百分比

enum 枚举

<attr name="type">
    <enum name="password" value="1" />
    <enum name="checkButton" value="2" />
    <enum name="phone" value="3" />
</attr>

其中,一般属性需要指定 nameformat,枚举属性只需指定 name,然后用 enum 标签指定所有可能属性的 namevalue(注:value 只能为 int 型)
获取值还是通过 getInt()

flag 位

<attr name="mode">
    <flag name="left_text" value="0x000001"/>
    <flag name="left_image" value="0x000010"/>
    <flag name="center_text" value="0x000100"/>
    <flag name="center_image" value="0x001000"/>
    <flag name="right_text" value="0x010000"/>
    <flag name="right_image" value="0x100000"/>
</attr>

java 代码:

int mode = ta.getInt(attr, -1);

AttributeSet 和 TypedArray

AttributeSet

保存的是该 View 声明的所有的属性

TypedArray

通过 AttributeSet 获取的值,如果是引用都变成了@+ 数字的字符串;TypedArray 其实是用来简化我们的工作的。
比如,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用 AttributeSet 去获得最终的像素值,那么需要第一步拿到 id,第二步再去解析 id。而 TypedArray 正是帮我们简化了这个过程。
如果通过 AttributeSet 获取最终的像素值的过程:

int widthDimensionId = attrs.getAttributeResourceValue(0, -1);
Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));

系统中定义的属性:sdk/platforms/android-xx/data/res/values

declare-styleable

就是帮我们在 R.java 中生成代码

public static final class attr {
    public static final int testAttr=0x7f0100a9;
}

public static final class styleable {
    public static final int test_android_text = 0;
    public static final int test_testAttr = 1;
    public static final int[] test = {
        0x0101014f, 0x7f0100a9
    };
}
    <attr name="test_attr" format="boolean"/>

    <declare-styleable name="EmptyView">
        <attr name="stringtest" format="string"/>
        <attr name="integertest" format="integer"/>

        <attr name="android:text"/>
        <attr name="test_attr"/>

        <attr name="emptyIcon" format="reference"/>
        <attr name="emptyDescText" format="string"/>
        <attr name="emptyDescTextSize" format="dimension"/>
        <attr name="emptyDescTextColor" format="color"/>
    </declare-styleable>
<attr name="textColor" format="reference|color" />

附:Android 中自定义属性的格式详解

1. reference:参考某一资源 ID

(1)属性定义:
        <declare-styleable name = "名称">
               <attr name = "background" format = "reference" />
        </declare-styleable>
(2)属性使用:
         <ImageView
                 android:layout_width = "42dip"
                 android:layout_height = "42dip"
                 android:background = "@drawable/图片ID"
                 />

2. color:颜色值

(1)属性定义:
        <declare-styleable name = "名称">
               <attr name = "textColor" format = "color" />
        </declare-styleable>
(2)属性使用:
        <TextView
                 android:layout_width = "42dip"
                 android:layout_height = "42dip"
                 android:textColor = "#00FF00"
                 />

3. boolean:布尔值

(1)属性定义:
        <declare-styleable name = "名称">
               <attr name = "focusable" format = "boolean" />
        </declare-styleable>
(2)属性使用:
        <Button
                android:layout_width = "42dip"
                android:layout_height = "42dip"
                android:focusable = "true"
                />

4. dimension:尺寸值

(1)属性定义:
        <declare-styleable name = "名称">
               <attr name = "layout_width" format = "dimension" />
        </declare-styleable>
(2)属性使用:
        <Button
                android:layout_width = "42dip"
                android:layout_height = "42dip"
                />

5. float:浮点值

(1)属性定义:
        <declare-styleable name = "AlphaAnimation">
               <attr name = "fromAlpha" format = "float" />
               <attr name = "toAlpha" format = "float" />
        </declare-styleable>
(2)属性使用:
        <alpha
               android:fromAlpha = "1.0"
               android:toAlpha = "0.7"
               />

6. integer:整型值

(1)属性定义:
        <declare-styleable name = "AnimatedRotateDrawable">
               <attr name = "visible" />
               <attr name = "frameDuration" format="integer" />
               <attr name = "framesCount" format="integer" />
               <attr name = "pivotX" />
               <attr name = "pivotY" />
               <attr name = "drawable" />
        </declare-styleable>
(2)属性使用:
        <animated-rotate
               xmlns:android = "http://schemas.android.com/apk/res/android" 
               android:drawable = "@drawable/图片ID" 
               android:pivotX = "50%" 
               android:pivotY = "50%" 
               android:framesCount = "12" 
               android:frameDuration = "100"
               />

7. string:字符串

(1)属性定义:
        <declare-styleable name = "MapView">
               <attr name = "apiKey" format = "string" />
        </declare-styleable>
(2)属性使用:
        <com.google.android.maps.MapView
                android:layout_width = "fill_parent"
                android:layout_height = "fill_parent"
                android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
                />

8. fraction:百分数

(1)属性定义:
        <declare-styleable name="RotateDrawable">
               <attr name = "visible" />
               <attr name = "fromDegrees" format = "float" />
               <attr name = "toDegrees" format = "float" />
               <attr name = "pivotX" format = "fraction" />
               <attr name = "pivotY" format = "fraction" />
               <attr name = "drawable" />
        </declare-styleable>
(2)属性使用:
        <rotate  xmlns:android = "http://schemas.android.com/apk/res/android"

android:interpolator = "@anim/动画 ID"
android:fromDegrees = "0"
               android:toDegrees = "360"
android:pivotX = "200%"
android:pivotY = "300%"
               android:duration = "5000"
android:repeatMode = "restart"
android:repeatCount = "infinite"
/>

9. enum:枚举值

(1)属性定义:
        <declare-styleable name="名称">
               <attr name="orientation">
                      <enum name="horizontal" value="0" />
                      <enum name="vertical" value="1" />
               </attr>           
        </declare-styleable>
(2)属性使用:
        <LinearLayout
                xmlns:android = "http://schemas.android.com/apk/res/android"
                android:orientation = "vertical"
                android:layout_width = "fill_parent"
                android:layout_height = "fill_parent"
                >
        </LinearLayout>

10. flag:位或运算

 (1)属性定义:
         <declare-styleable name="名称">
                <attr name="windowSoftInputMode">
                        <flag name = "stateUnspecified" value = "0" />
                        <flag name = "stateUnchanged" value = "1" />
                        <flag name = "stateHidden" value = "2" />
                        <flag name = "stateAlwaysHidden" value = "3" />
                        <flag name = "stateVisible" value = "4" />
                        <flag name = "stateAlwaysVisible" value = "5" />
                        <flag name = "adjustUnspecified" value = "0x00" />
                        <flag name = "adjustResize" value = "0x10" />
                        <flag name = "adjustPan" value = "0x20" />
                        <flag name = "adjustNothing" value = "0x30" />
                 </attr>        
         </declare-styleable>
 (2)属性使用:
        <activity
               android:name = ".StyleAndThemeActivity"
               android:label = "@string/app_name"
               android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
               <intent-filter>
                      <action android:name = "android.intent.action.MAIN" />
                      <category android:name = "android.intent.category.LAUNCHER" />
               </intent-filter>

         </activity>

注意: 属性定义时可以指定多种类型值

(1)属性定义:
        <declare-styleable name = "名称">
               <attr name = "background" format = "reference|color" />
        </declare-styleable>
(2)属性使用:
         <ImageView
                 android:layout_width = "42dip"
                 android:layout_height = "42dip"
                 android:background = "@drawable/图片ID|#00FF00"
                 />

很多自定义属性模板代码

<declare-styleable name="CommonToolbar">
    <attr name="toolbar_mode">
        <flag name="comon" value="0x000110"/>
        <flag name="left_text" value="0x000001"/>
        <flag name="left_image" value="0x000010"/>
        <flag name="center_text" value="0x000100"/>
        <flag name="center_image" value="0x001000"/>
        <flag name="right_text" value="0x010000"/>
        <flag name="right_image" value="0x100000"/>
    </attr>
    <attr name="left_titleText" format="string"/>
    <attr name="left_titleColor" format="color"/>
    <attr name="left_titleTextSize" format="dimension"/>
    <attr name="left_titleIcon" format="reference"/>
    <attr name="center_titleText" format="string"/>
    <attr name="center_titleColor" format="color"/>
    <attr name="center_titleTextSize" format="dimension"/>
    <attr name="center_titleIcon" format="reference"/>
    <attr name="right_titleText" format="string"/>
    <attr name="right_titleColor" format="color"/>
    <attr name="right_titleTextSize" format="dimension"/>
    <attr name="right_titleIcon" format="reference"/>
    <attr name="left_padding_left" format="dimension"/>
    <attr name="right_padding_right" format="dimension"/>
    <attr name="left_margin_left" format="dimension"/>
    <attr name="right_margin_right" format="dimension"/>
</declare-styleable>
private void initAttrs(Context context, AttributeSet attrs) {
    int defaultTextSize = dip2px(getContext(), DEFAULT_TEXT_SIZE); // android 的默认字体大小为12.0sp

    int defaultTextColorPrimary = getTextColorPrimary();
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CommonToolbar);

    try {
        int indexCount = ta.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = ta.getIndex(i);
            switch (attr) {
                case R.styleable.CommonToolbar_toolbar_mode:
                    int mode = ta.getInt(attr, -1);
                    switchMode(mode);
                    break;
                case R.styleable.CommonToolbar_left_titleText:
                    String leftTitleText = ta.getString(attr);
                    setText(mTvLeftTitle, leftTitleText);
                    break;
                case R.styleable.CommonToolbar_left_titleIcon:
                    mLeftTitleDrawableResId = ta.getResourceId(attr, DEFAULT_LEFT_ICON_RES);
                    break;
                case R.styleable.CommonToolbar_left_titleColor:
                    mLeftTextColor = ta.getColor(attr, defaultTextColorPrimary);
                    break;
                case R.styleable.CommonToolbar_left_titleTextSize:
                    mLeftTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
                    break;
                case R.styleable.CommonToolbar_center_titleText:
                    String centerTitleText = ta.getString(attr);
                    setText(mTvCenterTitle, centerTitleText);
                    break;
                case R.styleable.CommonToolbar_center_titleIcon:
                    mCenterTitleDrawableResId = ta.getResourceId(attr, DEFAULT_CENTER_ICON_RES);
                    break;
                case R.styleable.CommonToolbar_center_titleColor:
                    mCenterTextColor = ta.getColor(attr, defaultTextColorPrimary);
                    break;
                case R.styleable.CommonToolbar_center_titleTextSize:
                    mCenterTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
                    break;
                case R.styleable.CommonToolbar_right_titleText:
                    String rightTitleText = ta.getString(attr);
                    setText(mTvRightTitle, rightTitleText);
                    break;
                case R.styleable.CommonToolbar_right_titleIcon:
                    mRightTitleDrawableResId = ta.getResourceId(attr, DEFAULT_RIGHT_ICON_RES);
                    break;
                case R.styleable.CommonToolbar_right_titleColor:
                    mRightTextColor = ta.getColor(attr, defaultTextColorPrimary);
                    break;
                case R.styleable.CommonToolbar_right_titleTextSize:
                    mRightTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
                    break;
                case R.styleable.CommonToolbar_left_padding_left:
                    mLeftPaddingleft = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_PADDING_DP));
                    break;
                case R.styleable.CommonToolbar_right_padding_right:
                    mRightPaddingRight = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_PADDING_DP));
                    break;
                case R.styleable.CommonToolbar_left_margin_left:
                    mLeftMarginLeft = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_MAGIN_DP));
                    break;
                case R.styleable.CommonToolbar_right_margin_right:
                    mRightMarginRight = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_MAGIN_DP));
                    break;
                default:
                    break;
            }
        }
    } finally {
        ta.recycle();
    }
}

Reference

比较全面
http://blog.csdn.net/mybeta/article/details/39962235

format 格式介绍比较详细
http://www.cnblogs.com/zhangs1986/p/3243040.html